package inutan.gateway;

import inutan.gateway.balancer.ConsistentHashConsumerLoadBalancer;
import org.ricks.boot.AbstractBootstrap;
import org.ricks.common.ModularContext;
import org.ricks.common.event.EventBus;
import org.ricks.common.lang.Logger;
import org.ricks.common.utils.TimeUtils;
import org.ricks.net.AioSession;
import org.ricks.net.NetContext;
import org.ricks.net.balancer.IConsumerLoadBalancer;
import org.ricks.net.rpc.Consumer;
import org.ricks.net.rpc.ConsumerConfig;
import org.ricks.net.rpc.ConsumerModule;
import org.ricks.net.rpc.ProtocolModule;
import org.ricks.protocol.Protocol;
import org.ricks.protocol.ProtocolManager;

import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author chenwei
 * @Description: 注解方式启动server 弊端 只能启动一个服务，但是项目需求可能启动多个server
 * 2022年12月2日 18:37:31 后面找时间这部分进行优化
 * @date 2020/7/914:12
 *
 *
 *一致性哈希（默认）：适用于有状态服务  ， 其实也做不到有状态  。
 * 比如网关路由游戏模块  玩家数据缓存在游戏节点  如果游戏节点动态增删 hash 一致性路由节点可能命中失败，
 * 如果A节点 玩家数据还没有入库，路由到B节点load  db  可能拿到的是玩家的老数据、所以在容灾 情况下  也没办法真正做好 有状态路由固定节点
 * 为了避免路由其它游戏节点 重新load db 玩家数据丢失问题，之前方案是在 http登录服 维护了节点 与 玩家的绑定关系， 登录服分配网关节点会带入绑定节点，
 * 绑定节点从session容器中找不到或者 session失效，重新hash一致性分配节点 并且缓存起来
 *
 * 最终选择方案 网关节点使用分布式缓存机制 Apache Ignite 强一致性事务缓存
 * 连接保持时：客户端始终与同一网关节点通信，不受负载均衡策略干扰。
 * 重连行为：
 * 若TCP连接未断开（仅应用层重连）→ 节点不变。
 * 若TCP连接断开后重建 → 重新分配节点（分配结果取决于负载均衡策略）。
 * 强制重分配条件：必须断开并重建Socket连接，域名负载均衡才会重新分配节点。
 * 实践方案：
 * 需会话绑定的场景（如游戏网关）→ 采用 IP哈希或一致性哈希策略。
 * 结合分布式缓存（如Ignite）存储会话数据，确保节点变化时业务无感。
 */
public class GatewayBootstrap extends AbstractBootstrap {

    @Override
    protected void onStart() {
        long startTime = TimeUtils.now();
        Set<Class<?>> classes = ModularContext.me().projectClasses();
        try {
            Set<Class<?>> protocolClasses = classes.stream().filter(c -> c.isAnnotationPresent(Protocol.class)).collect(Collectors.toSet());
            ProtocolManager.initProtocolAuto(protocolClasses);
            NetContext.me().init();
            ConsumerConfig consumerConfig = new ConsumerConfig();
            ConsumerModule consumerModule = new ConsumerModule(new ProtocolModule((byte) 1, "游戏服"), new ConsistentHashConsumerLoadBalancer());
            consumerConfig.setConsumers(List.of(consumerModule));
            NetContext.me().setConsumerConfig(consumerConfig);
            Consumer.me().init();
            EventBus.me().init();
            printBanner(); //初始化banner
        } finally {
            long endTime = TimeUtils.now();
            long costTime = endTime - startTime;
            System.out.println("============ app started on time:"+ costTime + " ms ===========");
        }
    }

    @Override
    protected void onStop() {
        EventBus.me().shutdown();
        Logger.info("进程已停止");
    }

}
